package GDoc::JobDispatcher;

use strict;
use warnings;

use lib '/govdoc/app/lib';

use Parallel::ForkManager;
use Proc::Daemon;
use Amazon::SQS::Simple;
use Module::Find;
use YAML::XS;
use Data::Dumper;

use GDoc::AWS '$AWS_ACCESS_KEY_ID', '$AWS_SECRET_KEY';
use GDoc::Logger;
use GDoc::Job;
use GDoc::Job::Fetch;

my $MAX_PROCS = 10;
my $FETCH_QUEUE_URL = 'https://queue.amazonaws.com/fetch';

my $logger = GDoc::Logger->get_logger;
              
my %JOBS;

sub run { 
    my $class = shift;

    $logger->info( "Starting up..." );

    $class->_load_job_classes;

    my $pm  = Parallel::ForkManager->new( $MAX_PROCS );
    my $sqs = Amazon::SQS::Simple->new( $AWS_ACCESS_KEY_ID, $AWS_SECRET_KEY );
    my $q   = $sqs->GetQueue( $FETCH_QUEUE_URL );

#    Proc::Daemon::Init;

    # now we have to re-open our filehandles
    $logger = GDoc::Logger->get_logger( reopen => 1 );

    $logger->info( "Daemonized" );

    while( 1 ) {
        my $sqs_msg;
        eval { 
            $sqs_msg = $q->ReceiveMessage;
        };

        # sometimes SQS blows up and returns a HTTP 5xx error
        if ( my $err = $@ ) { 
            $logger->error( "SQS Error: $err" );
            sleep 5;
            next;
        }

        unless ( $sqs_msg ) { 
            sleep 5;
            next;
        }

        my $msg = YAML::XS::Load( $sqs_msg->MessageBody );
        die "No job class specified" unless $msg->{class};
        my $jbclass = 'GDoc::Job::' . $msg->{class};
        
        if ( $msg->{reload} ) { 
            my $file = ( join '/', split /::/, $jbclass ) . '.pm';
            delete $JOBS{$jbclass};
            delete $INC{$file};
        }

        # if a class was added after we started, or needs to be reloaded, try loading it
        unless( $JOBS{$jbclass} ) { 
            $logger->warn( "Reloading job classes to look for $jbclass" );
            $class->_load_job_classes;
            
            unless( $JOBS{$jbclass} ) { 
                $logger->error( "Job class $jbclass could not be loaded" );
                return;
            }
        }

        # fork a new process to run the job
        my $pid = $pm->start and next;
        eval { 
            $class->_run_job( $msg );
        };
        
        if ( my $err = $@ ) { 
            $logger->error( "Could not run job: $err" );
            # NOTE: add an exception handler so jobs can relaunch themselves?
        }
        
        $q->DeleteMessage( $sqs_msg->ReceiptHandle );
        
        sleep 1;
        $pm->finish;
    }
}


# loads all GDoc::Job::* classes
sub _load_job_classes { 
    my $class = shift;

    %JOBS = map { $_ => 1 } useall( 'GDoc::Job' );
}


sub _run_job { 
    my $class = shift;
    my $msg   = shift;

    my $data = $msg->{data} || { };
    my $jbclass = 'GDoc::Job::' . $msg->{class};
    my $job  = $jbclass->new( data => $data );
    $job->setup;
    $job->run;
}
   

__PACKAGE__->run unless caller;


1;
    
